<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
	<script>
		var isLocalStorage = false;
		var mxLoadStylesheets = false;
	</script>
	<!-- CSS for print output is needed for using current window -->
	<style type="text/css">
		@media print {
		  table.mxPageSelector { display: none; }
		  hr.mxPageBreak { display: none; }
		}
		@media screen {
		  table.mxPageSelector { position: fixed; right: 10px; top: 10px;font-family: Arial; font-size:10pt; border: solid 1px darkgray;background: white; border-collapse:collapse; }
		  table.mxPageSelector td { border: solid 1px gray; padding:4px; }
		  body.mxPage { background: gray; }
		}
	</style>
	<link rel="stylesheet" href="mxgraph/css/common.css" charset="UTF-8" type="text/css">
    <script src="js/app.min.js"></script>
	<script>
		Editor.initMath();
	
		function render(data)
		{
			var autoScale = false;
			
			if (data.scale == 'auto')
			{
				autoScale = true;
				data.scale = 1;
			}
			
			var graph = new Graph(document.getElementById('graph'));
			data.border = parseInt(data.border) || 0;
			data.w = parseFloat(data.w) || 0;
			data.h = parseFloat(data.h) || 0;
			data.scale = parseFloat(data.scale) || 1;
			
			var extras = null;
			
			try
			{
				extras = JSON.parse(data.extras);
			} 
			catch(e){}

			// Parses XML
			var xmlDoc = mxUtils.parseXml(data.xml);
			var diagrams = null;
			var from = 0;
			
			// Handles mxfile
			if (xmlDoc.documentElement.nodeName == 'mxfile')
			{
				diagrams = xmlDoc.documentElement.getElementsByTagName('diagram');
			}
	        
	        /**
			 * Implements %page% and %pagenumber% placeholders
			 */
			var graphGetGlobalVariable = graph.getGlobalVariable;
			
			graph.getGlobalVariable = function(name)
			{
				if (name == 'page')
				{
					return (diagrams == null) ? 'Page-1' :
						(diagrams[from].getAttribute('name') || ('Page-' + (from + 1)));
				}
				else if (name == 'pagenumber')
				{
					return from + 1;
				}
				
				return graphGetGlobalVariable.apply(this, arguments);
			};
			
			/**
			 * Disables custom links on shapes.
			 */
			var graphGetLinkForCell = graph.getLinkForCell;
			
			graph.getLinkForCell = function(cell)
			{
				var link = graphGetLinkForCell.apply(this, arguments);
				
				if (link != null && this.isCustomLink(link))
				{
					link = null;
				}
				
				return link;
			};
			
			/**
			 * Disables custom links in labels.
			 */
			var cellRendererRedrawLabelShape = graph.cellRenderer.redrawLabelShape;
			
			graph.cellRenderer.redrawLabelShape = function(shape)
			{
				cellRendererRedrawLabelShape.apply(this, arguments);
				
				if (shape.node != null)
				{
					var links = shape.node.getElementsByTagName('a');

					for (var i = 0; i < links.length; i++)
					{
						var href = links[i].getAttribute('href');
						
						if (href != null && graph.isCustomLink(href))
						{
							links[i].setAttribute('href', '#');
						}
					}	
				}
			};
			
			var preview = null;
			var waitCounter = 1;
			var bounds;
			var pageId;
			var expScale;
			// Waits for all images to finish loading
			var cache = new Object();
			var math = false;
			
			// Decrements waitCounter and invokes callback when finished
			function decrementWaitCounter()
			{
				if (--waitCounter < 1)
				{
					var doneDiv = document.createElement("div");
					doneDiv.id = 'LoadingComplete';
					doneDiv.style.display = 'none';
					doneDiv.setAttribute('bounds', JSON.stringify(bounds));
					doneDiv.setAttribute('page-id', pageId);
					doneDiv.setAttribute('scale', expScale);
					document.body.appendChild(doneDiv);
				}
			};
			
			function waitForImages(tagName, attributeName)
			{
				var imgs = document.body.getElementsByTagName(tagName);
				waitCounter += imgs.length;

				for (var i = 0; i < imgs.length; i++)
				{
					// No load events for image elements in Phantom using indirection instead
					var src = imgs[i].getAttribute(attributeName);
					
					if (src != null && src.length > 0 && cache[src] == null)
					{
						cache[src] = new Image();
						cache[src].onload = decrementWaitCounter;
						cache[src].onerror = decrementWaitCounter;
						cache[src].src = src;
					}
					else
					{
						decrementWaitCounter();
					}
				}
			};
			
			// Waits for MathJax.Hub to become available to register
			// wait counter callback asynchronously after math render
			var editorDoMathJaxRender = Editor.doMathJaxRender;
			
			Editor.doMathJaxRender = function(container)
			{
				editorDoMathJaxRender.apply(this, arguments);
				
				window.setTimeout(function()
				{
					window.MathJax.Hub.Queue(function ()
					{
						decrementWaitCounter();
					});
				}, 0);
			};
			
			// Adds async MathJax rendering task
			function renderMath(elt)
			{
				if (math && Editor.MathJaxRender != null)
				{
					waitCounter++;
					Editor.MathJaxRender(elt);
				}
			};

			function renderPage()
			{
				// Enables math typesetting
				math |= xmlDoc.documentElement.getAttribute('math') == '1';
				
				if (math)
				{
					mxClient.NO_FO = true;
				}
	
				// Configure graph
				graph.foldingEnabled = false;
				graph.setEnabled(false);
				
				// Sets background image
				var bgImg = xmlDoc.documentElement.getAttribute('backgroundImage');
	
				if (bgImg != null)
				{
					bgImg = JSON.parse(bgImg);
					graph.setBackgroundImage(new mxImage(bgImg.src, bgImg.width, bgImg.height));
				}
				
				// Parses XML into graph
				var codec = new mxCodec(xmlDoc);
				var model = graph.getModel();
				codec.decode(xmlDoc.documentElement, model);
	
				// Loads background color
				var bg = (data.bg != null && data.bg.length > 0) ? data.bg : xmlDoc.documentElement.getAttribute('background');
	
				// Normalizes values for transparent backgrounds
				if (bg == 'none' || bg == '')
				{
					bg = null;
				}
				
				// Checks if export format supports transparent backgrounds
				if (bg == null && data.format != 'gif' && data.format != 'png')
				{
					bg = '#ffffff';
				}
	
				// Sets background color on page
				if (bg != null)
				{
					document.body.style.backgroundColor = bg;
				}

				//handle layers
				if (extras != null && extras.layers != null)
				{
					var childCount = model.getChildCount(model.root);
					
					// Hides all layers
					for (var i = 0; i < childCount; i++)
					{
						model.setVisible(model.getChildAt(model.root, i), false);
					}
					
					for (var i = 0; i < extras.layers.length; i++)
					{
						var layer = model.getChildAt(model.root, extras.layers[i]);
						
						if (layer != null)
						{
							model.setVisible(layer, true);
						}
					}
				}
				
				// Sets initial value for PDF page background
				graph.pdfPageVisible = false;
				
				// Handles PDF output where the output should match the page format if the page is visible
				if (data.format == 'pdf' && xmlDoc.documentElement.getAttribute('page') == '1' && data.w == 0 && data.h == 0)
				{
					var pw = xmlDoc.documentElement.getAttribute('pageWidth');
					var ph = xmlDoc.documentElement.getAttribute('pageHeight');
					graph.pdfPageVisible = true;
					
					if (pw != null && ph != null)
					{
						graph.pageFormat = new mxRectangle(0, 0, parseFloat(pw), parseFloat(ph));
					}
					
					var ps = xmlDoc.documentElement.getAttribute('pageScale');
					
					if (ps != null)
					{
						graph.pageScale = ps;
					}
					
					graph.getPageSize = function()
					{
						return new mxRectangle(0, 0, this.pageFormat.width * this.pageScale,
							this.pageFormat.height * this.pageScale);
					};
					
					graph.getPageLayout = function()
					{
						var size = this.getPageSize();
						var bounds = this.getGraphBounds();
						
						if (bounds.width == 0 || bounds.height == 0)
						{
							return new mxRectangle(0, 0, 1, 1);
						}
						else
						{
							// Computes untransformed graph bounds
							var x = Math.ceil(bounds.x / this.view.scale - this.view.translate.x);
							var y = Math.ceil(bounds.y / this.view.scale - this.view.translate.y);
							var w = Math.floor(bounds.width / this.view.scale);
							var h = Math.floor(bounds.height / this.view.scale);
							
							var x0 = Math.floor(x / size.width);
							var y0 = Math.floor(y / size.height);
							var w0 = Math.ceil((x + w) / size.width) - x0;
							var h0 = Math.ceil((y + h) / size.height) - y0;
							
							return new mxRectangle(x0, y0, w0, h0);
						}
					};
	
					// Fits the number of background pages to the graph
					graph.view.getBackgroundPageBounds = function()
					{
						var layout = this.graph.getPageLayout();
						var page = this.graph.getPageSize();
						
						return new mxRectangle(this.scale * (this.translate.x + layout.x * page.width),
							this.scale * (this.translate.y + layout.y * page.height),
							this.scale * layout.width * page.width,
							this.scale * layout.height * page.height);
					};
				}
	
				if (!graph.pdfPageVisible)
				{
					var b = graph.getGraphBounds();
					
					// Floor is needed to keep rendering crisp
					if (data.w > 0 && data.h > 0)
					{
						var s = Math.min(data.w / b.width, data.h / b.height);
						graph.view.scaleAndTranslate(s,
							Math.floor(data.border / s - Math.floor(b.x)),
							Math.floor(data.border / s - Math.floor(b.y)));
					}
					else
					{
						var s = data.scale;
						
						if (autoScale)
						{
							var pageWidth = (extras != null && extras.pageWidth != null) ? extras.pageWidth : 800;
							
							if (b.width < pageWidth & b.height < 1.5 * pageWidth)
							{
								s = 4;
							}
							else if (b.width < 2 * pageWidth & b.height < 3 * pageWidth)
							{
								s = 3;
							}
							else if (b.width < 4 * pageWidth && b.height < 6 * pageWidth)
							{
								s = 2;
							}
						}
						
						graph.view.scaleAndTranslate(s,
							Math.floor(data.border - Math.floor(b.x)),
							Math.floor(data.border - Math.floor(b.y)));
					}
				}
				else
				{
					// Disables border for PDF page export
					data.border = 0;
					
					// Moves to first page in page layout
					var layout = graph.getPageLayout();
					var page = graph.getPageSize();
					var dx = layout.x * page.width;
					var dy = layout.y * page.height;
					
					if (dx != 0 || dy != 0)
					{
						graph.view.setTranslate(Math.floor(-dx), Math.floor(-dy));
					}
				}
				
				// Gets the diagram bounds and sets the document size
				bounds = (graph.pdfPageVisible) ? graph.view.getBackgroundPageBounds() : graph.getGraphBounds();
				bounds.width = Math.ceil(bounds.width + data.border) + 1; //The 1 extra pixels to prevent cutting the cells on the edges when crop is enabled
				bounds.height = Math.ceil(bounds.height + data.border);
				expScale = graph.view.scale || 1;
				
				// Converts the graph to a vertical sequence of pages for PDF export
				if (graph.pdfPageVisible)
				{
					var pf = graph.pageFormat || mxConstants.PAGE_FORMAT_A4_PORTRAIT;
					var scale = 1 / graph.pageScale;
					var autoOrigin = false;
					var border = 0;
	
					// Negative coordinates are cropped or shifted if page visible
					var gb = graph.getGraphBounds();
					var x0 = 0;
					var y0 = 0;
			
					// Applies print scale
					pf = mxRectangle.fromRectangle(pf);
					pf.width = Math.ceil(pf.width) + 1; //The 1 extra pixels to prevent cutting the cells on the right edge of the page
					pf.height = Math.ceil(pf.height);
					
					// Starts at first visible page
					var layout = graph.getPageLayout();
					x0 -= layout.x * pf.width;
					y0 -= layout.y * pf.height;
					
					if (preview == null)
					{
						preview = new mxPrintPreview(graph, scale, pf, border, x0, y0);
						preview.printBackgroundImage = true;
						preview.autoOrigin = autoOrigin;
						preview.backgroundColor = bg;
						// Renders print output into this document and removes the graph container
						preview.open(null, window);
						graph.container.parentNode.removeChild(graph.container);
					}
					else
					{
						preview.backgroundColor = bg;
						preview.autoOrigin = autoOrigin; 
						preview.appendGraph(graph, scale, x0, y0);
					}
					// Adds shadow
					// NOTE: Shadow rasterizes output
					/*if (mxClient.IS_SVG && xmlDoc.documentElement.getAttribute('shadow') == '1')
					{
						var svgs = document.getElementsByTagName('svg');
						
						for (var i = 0; i < svgs.length; i++)
						{
							var svg = svgs[i];
	
							var filter = graph.addSvgShadow(svg, null, true);
							filter.setAttribute('id', 'shadow-' + i);
							svg.appendChild(filter);
							svg.setAttribute('filter', 'url(#' + 'shadow-' + i + ')');
						}
						
						border = 7;
					}*/
					
					bounds = new mxRectangle(0, 0, pf.width, pf.height);
				}
				else
				{
					// Adds shadow
					// NOTE: PDF shadow rasterizes output so it's disabled
					if (data.format != 'pdf' && mxClient.IS_SVG && xmlDoc.documentElement.getAttribute('shadow') == '1')
					{
						graph.addSvgShadow(graph.view.canvas.ownerSVGElement, null, true);
						graph.setShadowVisible(true);
						bounds.width += 7;
						bounds.height += 7;
					}
					
					document.body.style.width = Math.ceil(bounds.x + bounds.width) + 'px';
					document.body.style.height = Math.ceil(bounds.y + bounds.height) + 'px';
				}
			}
			
			if (diagrams != null && diagrams.length > 0)
			{
				var to = diagrams.length - 1;
				
				//Parameters to and all pages should not be sent with formats other than PDF with page view enabled
				if (!data.allPages)
				{
					if (data.pageId != null)
					{
						for (var i = 0; i < diagrams.length; i++)
						{
							if (data.pageId == diagrams[i].getAttribute('id'))
							{
								from = i;
								to = i;
								break;
							}
						}
					}
					else
					{
						from = Math.max(0, Math.min(parseInt(data.from) || from, diagrams.length - 1));
						to = parseInt(data.to);
						//If to is not defined, use from (so one page), otherwise, to is restricted to the range from "from" to diagrams.length - 1
						to = isNaN(to)? from : Math.max(from, Math.min(to, diagrams.length - 1));
					}
				}
					
				for (var i = from; i <= to; i++) 
				{
					if (diagrams[i] != null)
					{
						if (pageId == null)
						{
							pageId = diagrams[i].getAttribute('id')
						}
						
						xmlDoc = mxUtils.parseXml(Graph.decompress(mxUtils.getTextContent(diagrams[i])));
						graph.getModel().clear();
						from = i;
						renderPage();
					}
				}
			}
			else
			{
				renderPage();
			}
			
			// Includes images in SVG and HTML labels
			waitForImages('image', 'xlink:href');
			waitForImages('img', 'src');
			renderMath(document.body);
			// Immediate return if not waiting for any content
			decrementWaitCounter();
		};
	</script>
</head>
<body style="margin:0px;">
	<div id="graph" style="width:100%;height:100%;"></div>
</body>
</html>
